using JGSY.CMS.LowCode.Platform.Application.Interfaces;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace JGSY.CMS.LowCode.Platform.Infrastructure.Services
{
    /// <summary>
    /// 身份验证服务实现
    /// 提供用户注册、登录、JWT令牌管理和密码重置等身份验证功能的具体实现
    /// </summary>
    public class AuthenticationService : IAuthenticationAppService
    {
        /// <summary>
        /// 用户仓储接口
        /// </summary>
        private readonly Domain.Interfaces.IUserRepository _userRepository;
        
        /// <summary>
        /// 日志记录器
        /// </summary>
        private readonly ILogger<AuthenticationService> _logger;
        
        /// <summary>
        /// 缓存服务
        /// </summary>
        private readonly ICacheService _cacheService;
        
        /// <summary>
        /// 用户上下文服务
        /// </summary>
        private readonly IUserContextService _userContextService;
        
        /// <summary>
        /// JWT密钥
        /// </summary>
        private readonly string _jwtSecret = "your_jwt_secret_key_must_be_at_least_256_bits_long_for_security";
        
        /// <summary>
        /// JWT令牌过期天数
        /// </summary>
        private readonly int _jwtExpireDays = 7;

        /// <summary>
        /// 初始化身份验证服务实例
        /// </summary>
        /// <param name="userRepository">用户仓储接口实例</param>
        /// <param name="logger">日志记录器</param>
        /// <param name="cacheService">缓存服务</param>
        /// <param name="userContextService">用户上下文服务</param>
        public AuthenticationService(
            Domain.Interfaces.IUserRepository userRepository,
            ILogger<AuthenticationService> logger,
            ICacheService cacheService,
            IUserContextService userContextService)
        {
            _userRepository = userRepository;
            _logger = logger;
            _cacheService = cacheService;
            _userContextService = userContextService;
        }

        /// <summary>
        /// 用户注册
        /// 创建新用户账户，包括用户名、邮箱和密码验证
        /// </summary>
        /// <param name="username">用户名</param>
        /// <param name="email">邮箱地址</param>
        /// <param name="password">明文密码</param>
        /// <param name="phone">手机号码，可选</param>
        /// <returns>注册操作的异步任务</returns>
        public async Task RegisterAsync(string username, string email, string password, string? phone = null)
        {
            var hashedPassword = BCrypt.Net.BCrypt.HashPassword(password);
            var user = new Domain.Entities.User
            {
                Username = username,
                Email = email,
                PasswordHash = hashedPassword,
                Phone = phone,
                CreatedAt = DateTime.UtcNow
            };
            await _userRepository.AddAsync(user);
        }

        /// <summary>
        /// 用户登录
        /// 验证用户凭据并生成JWT令牌
        /// </summary>
        /// <param name="username">用户名</param>
        /// <param name="password">明文密码</param>
        /// <returns>成功时返回JWT令牌，失败时抛出异常</returns>
        /// <exception cref="UnauthorizedAccessException">用户名或密码错误时抛出</exception>
        public async Task<string> LoginAsync(string username, string password)
        {
            var user = await _userRepository.GetByUsernameAsync(username);
            if (user == null || !BCrypt.Net.BCrypt.Verify(password, user.PasswordHash))
                throw new UnauthorizedAccessException("用户名或密码错误");
            return GenerateJwtToken(user);
        }

        /// <summary>
        /// 刷新访问令牌
        /// 使用刷新令牌获取新的访问令牌
        /// </summary>
        /// <param name="refreshToken">刷新令牌</param>
        /// <returns>新的访问令牌</returns>
        public async Task<string> RefreshTokenAsync(string refreshToken)
        {
            try
            {
                _logger.LogInformation("开始刷新令牌，RefreshToken: {RefreshTokenPrefix}...", 
                    refreshToken.Length > 10 ? refreshToken[..10] : refreshToken);
                
                // 1. 验证刷新令牌格式和有效性
                if (string.IsNullOrWhiteSpace(refreshToken))
                {
                    _logger.LogWarning("刷新令牌为空");
                    throw new UnauthorizedAccessException("Invalid refresh token");
                }

                // 2. 从缓存中查找刷新令牌
                var cacheKey = $"refresh_token:{refreshToken}";
                var cachedUserId = await _cacheService.GetAsync<long?>(cacheKey);
                
                if (!cachedUserId.HasValue)
                {
                    _logger.LogWarning("刷新令牌不存在或已过期：{RefreshToken}", 
                        refreshToken.Length > 10 ? refreshToken[..10] : refreshToken);
                    throw new UnauthorizedAccessException("Refresh token expired or not found");
                }

                // 3. 获取用户信息
                var user = await _userRepository.GetByIdAsync(cachedUserId.Value);
                if (user == null || user.Status != 1)
                {
                    _logger.LogWarning("用户不存在或已禁用，用户ID：{UserId}", cachedUserId.Value);
                    throw new UnauthorizedAccessException("User not found or disabled");
                }

                // 4. 生成新的访问令牌
                var newAccessToken = GenerateJwtToken(user);
                
                // 5. 生成新的刷新令牌并更新缓存
                var newRefreshToken = GenerateRefreshToken();
                var oldCacheKey = cacheKey;
                var newCacheKey = $"refresh_token:{newRefreshToken}";
                
                // 删除旧的刷新令牌
                await _cacheService.RemoveAsync(oldCacheKey);
                
                // 存储新的刷新令牌（有效期7天）
                await _cacheService.SetAsync(newCacheKey, user.Id, TimeSpan.FromDays(7));

                _logger.LogInformation("令牌刷新成功，用户ID：{UserId}，用户名：{Username}", user.Id, user.Username);
                
                return newAccessToken;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "刷新令牌失败：{Error}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// 发送短信验证码
        /// 向指定手机号发送验证码用于身份验证
        /// </summary>
        /// <param name="phone">接收验证码的手机号码</param>
        /// <returns>发送操作的异步任务</returns>
        public async Task SendSmsCodeAsync(string phone)
        {
            try
            {
                _logger.LogInformation("开始发送短信验证码，手机号：{Phone}", phone);
                
                // 1. 验证手机号格式
                if (string.IsNullOrWhiteSpace(phone) || !System.Text.RegularExpressions.Regex.IsMatch(phone, @"^1[3-9]\d{9}$"))
                {
                    _logger.LogWarning("手机号格式不正确：{Phone}", phone);
                    throw new ArgumentException("手机号格式不正确");
                }

                // 2. 检查发送频率限制
                var rateLimitKey = $"sms_rate_limit:{phone}";
                var lastSendTime = await _cacheService.GetAsync<DateTime?>(rateLimitKey);
                if (lastSendTime.HasValue && DateTime.UtcNow.Subtract(lastSendTime.Value).TotalSeconds < 60)
                {
                    _logger.LogWarning("短信发送过于频繁，手机号：{Phone}", phone);
                    throw new InvalidOperationException("发送过于频繁，请60秒后再试");
                }

                // 3. 生成6位验证码
                var verificationCode = new Random().Next(100000, 999999).ToString();

                // 4. 存储验证码到缓存（有效期5分钟）
                var cacheKey = $"sms_verification:{phone}";
                await _cacheService.SetAsync(cacheKey, verificationCode, TimeSpan.FromMinutes(5));

                // 5. 模拟发送短信（开发环境）
                var message = $"【JGSY】您的验证码是：{verificationCode}，5分钟内有效。";
                _logger.LogInformation("【模拟短信发送】手机号：{Phone}，验证码：{Code}，内容：{Message}", phone, verificationCode, message);

                // 6. 设置发送频率限制
                await _cacheService.SetAsync(rateLimitKey, DateTime.UtcNow, TimeSpan.FromMinutes(1));

                _logger.LogInformation("短信验证码发送成功，手机号：{Phone}", phone);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "发送短信验证码失败，手机号：{Phone}，错误：{Error}", phone, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// 验证短信验证码
        /// 校验用户输入的短信验证码是否正确
        /// </summary>
        /// <param name="phone">接收验证码的手机号码</param>
        /// <param name="code">用户输入的验证码</param>
        /// <returns>验证成功返回true，失败返回false</returns>
        public async Task<bool> VerifySmsCodeAsync(string phone, string code)
        {
            try
            {
                _logger.LogInformation("开始验证短信验证码，手机号：{Phone}，验证码：{Code}", phone, code);
                
                // 1. 验证输入参数
                if (string.IsNullOrWhiteSpace(phone) || string.IsNullOrWhiteSpace(code))
                {
                    _logger.LogWarning("手机号或验证码为空，手机号：{Phone}", phone);
                    return false;
                }

                // 2. 从缓存获取验证码
                var cacheKey = $"sms_verification:{phone}";
                var cachedCode = await _cacheService.GetAsync<string>(cacheKey);

                if (string.IsNullOrEmpty(cachedCode))
                {
                    _logger.LogWarning("验证码已过期或不存在，手机号：{Phone}", phone);
                    return false;
                }

                // 3. 验证码比较
                if (cachedCode != code)
                {
                    _logger.LogWarning("验证码错误，手机号：{Phone}，期望：{Expected}，实际：{Actual}", phone, cachedCode, code);
                    return false;
                }

                // 4. 验证成功，删除验证码
                await _cacheService.RemoveAsync(cacheKey);

                _logger.LogInformation("短信验证码验证成功，手机号：{Phone}", phone);
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "验证短信验证码失败，手机号：{Phone}，错误：{Error}", phone, ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 请求密码重置
        /// 向用户邮箱发送密码重置验证码
        /// </summary>
        /// <param name="email">用户邮箱地址</param>
        /// <returns>请求操作的异步任务</returns>
        public async Task RequestPasswordResetAsync(string email)
        {
            try
            {
                _logger.LogInformation("开始处理密码重置请求，邮箱：{Email}", email);
                
                // 1. 验证邮箱格式
                if (string.IsNullOrWhiteSpace(email) || !System.Text.RegularExpressions.Regex.IsMatch(email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$"))
                {
                    _logger.LogWarning("邮箱格式不正确：{Email}", email);
                    throw new ArgumentException("邮箱格式不正确");
                }

                // 2. 检查用户是否存在
                var user = await _userRepository.GetByEmailAsync(email);
                if (user == null)
                {
                    // 安全考虑：不透露用户是否存在，但记录日志
                    _logger.LogWarning("密码重置请求的邮箱不存在：{Email}", email);
                    // 返回成功以不泄露用户信息
                    return;
                }

                // 3. 检查发送频率限制
                var rateLimitKey = $"password_reset_rate_limit:{email}";
                var lastSendTime = await _cacheService.GetAsync<DateTime?>(rateLimitKey);
                if (lastSendTime.HasValue && DateTime.UtcNow.Subtract(lastSendTime.Value).TotalMinutes < 5)
                {
                    _logger.LogWarning("密码重置请求过于频繁，邮箱：{Email}", email);
                    throw new InvalidOperationException("发送过于频繁，请5分钟后再试");
                }

                // 4. 生成6位重置码
                var resetCode = new Random().Next(100000, 999999).ToString();

                // 5. 存储重置码到缓存（有效期15分钟）
                var cacheKey = $"password_reset:{email}";
                await _cacheService.SetAsync(cacheKey, resetCode, TimeSpan.FromMinutes(15));

                // 6. 模拟发送邮件（开发环境）
                var subject = "【JGSY CMS】密码重置验证码";
                var body = $"您好！您正在进行密码重置操作，验证码为：{resetCode}，有效期15分钟。";
                _logger.LogInformation("【模拟邮件发送】收件人：{Email}，主题：{Subject}，验证码：{Code}", email, subject, resetCode);

                // 7. 设置发送频率限制
                await _cacheService.SetAsync(rateLimitKey, DateTime.UtcNow, TimeSpan.FromMinutes(5));

                _logger.LogInformation("密码重置验证码发送成功，邮箱：{Email}", email);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "发送密码重置验证码失败，邮箱：{Email}，错误：{Error}", email, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// 确认密码重置
        /// 使用验证码重置用户密码
        /// </summary>
        /// <param name="email">用户邮箱地址</param>
        /// <param name="code">密码重置验证码</param>
        /// <param name="newPassword">新密码</param>
        /// <returns>重置成功返回true，失败返回false</returns>
        public async Task<bool> ConfirmPasswordResetAsync(string email, string code, string newPassword)
        {
            var user = await _userRepository.GetByEmailAsync(email);
            if (user == null) return false;
            user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(newPassword);
            await _userRepository.UpdateAsync(user);
            return true;
        }

        /// <summary>
        /// 获取当前用户信息
        /// 从认证上下文中获取当前登录用户的详细信息
        /// </summary>
        /// <returns>当前用户DTO，如果用户未登录则返回null</returns>
        public async Task<Application.DTOs.UserDto?> GetCurrentUserAsync()
        {
            try
            {
                _logger.LogInformation("获取当前用户信息");
                
                // 1. 从用户上下文获取用户ID
                var currentUserId = _userContextService.GetCurrentUserId();
                if (!currentUserId.HasValue)
                {
                    _logger.LogInformation("当前无用户登录");
                    return null;
                }

                // 2. 获取用户详细信息
                var user = await _userRepository.GetByIdAsync(currentUserId.Value);
                if (user == null || user.Status != 1)
                {
                    _logger.LogWarning("当前用户不存在或已禁用，用户ID：{UserId}", currentUserId);
                    return null;
                }

                // 3. 转换为DTO
                var userDto = new Application.DTOs.UserDto
                {
                    Id = user.Id,
                    Username = user.Username,
                    Email = user.Email ?? "",
                    Phone = user.Phone,
                    RealName = user.RealName,
                    Avatar = user.Avatar,
                    Status = user.Status,
                    LastLoginTime = user.LastLoginTime,
                    DepartmentId = user.DepartmentId,
                    CreatedAt = user.CreatedAt,
                    UpdatedAt = user.UpdatedAt
                };

                _logger.LogInformation("获取当前用户信息成功，用户ID：{UserId}，用户名：{Username}", user.Id, user.Username);
                return userDto;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取当前用户信息失败：{Error}", ex.Message);
                return null;
            }
        }

        /// <summary>
        /// 用户登出
        /// 清理用户会话和相关的认证信息
        /// </summary>
        /// <returns>登出操作的异步任务</returns>
        public async Task LogoutAsync()
        {
            try
            {
                _logger.LogInformation("用户登出操作");
                
                // 1. 从用户上下文获取当前用户信息
                var currentUserId = _userContextService.GetCurrentUserId();
                if (currentUserId.HasValue)
                {
                    // 2. 清理该用户的所有刷新令牌缓存
                    await ClearUserRefreshTokensAsync(currentUserId.Value);
                    
                    _logger.LogInformation("已清理用户刷新令牌缓存，用户ID：{UserId}", currentUserId);
                }

                // 3. 获取当前访问令牌（如果存在）
                // 由于UserContextService没有GetAccessToken方法，我们跳过令牌黑名单处理
                // 在实际生产环境中，可能需要添加GetAccessToken方法或使用其他方式获取当前令牌
                
                _logger.LogInformation("用户登出成功");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "用户登出失败：{Error}", ex.Message);
                throw;
            }
        }
        
        /// <summary>
        /// 清理用户的所有刷新令牌
        /// </summary>
        /// <param name="userId">用户ID</param>
        private async Task ClearUserRefreshTokensAsync(long userId)
        {
            try
            {
                // 这里我们使用通配符模式来清理特定用户的所有刷新令牌
                // 注意：这需要缓存服务支持模式删除，或者我们需要维护一个用户刷新令牌列表
                var refreshTokenKey = $"refresh_token:user:{userId}:*";
                
                // 由于我们的ICacheService没有删除模式的方法，我们可以尝试删除已知的刷新令牌
                // 在实际实现中，可能需要扩展缓存服务或维护刷新令牌的索引
                
                _logger.LogInformation("清理用户刷新令牌，用户ID：{UserId}", userId);

                // 保持异步方法签名的一致性
                await Task.CompletedTask;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "清理用户刷新令牌失败，用户ID：{UserId}，错误：{Error}", userId, ex.Message);
            }
        }

        /// <summary>
        /// 生成JWT令牌
        /// 为指定用户创建包含用户身份信息的JWT令牌
        /// </summary>
        /// <param name="user">用户实体</param>
        /// <returns>生成的JWT令牌字符串</returns>
        private string GenerateJwtToken(Domain.Entities.User user)
        {
            var claims = new[]
            {
                new System.Security.Claims.Claim(System.IdentityModel.Tokens.Jwt.JwtRegisteredClaimNames.Sub, user.Id.ToString()),
                new System.Security.Claims.Claim(System.IdentityModel.Tokens.Jwt.JwtRegisteredClaimNames.Email, user.Email ?? ""),
                new System.Security.Claims.Claim("username", user.Username ?? "")
            };
            var key = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(_jwtSecret));
            var creds = new Microsoft.IdentityModel.Tokens.SigningCredentials(key, Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256);
            var token = new System.IdentityModel.Tokens.Jwt.JwtSecurityToken(
                issuer: "JGSY.CMS.LowCode.Platform",
                audience: "JGSY.CMS.LowCode.Platform",
                claims: claims,
                expires: System.DateTime.UtcNow.AddDays(_jwtExpireDays),
                signingCredentials: creds
            );
            return new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler().WriteToken(token);
        }

        /// <summary>
        /// 生成刷新令牌
        /// 创建一个安全的随机刷新令牌
        /// </summary>
        /// <returns>刷新令牌字符串</returns>
        private string GenerateRefreshToken()
        {
            var randomBytes = new byte[32];
            using var rng = RandomNumberGenerator.Create();
            rng.GetBytes(randomBytes);
            return Convert.ToBase64String(randomBytes);
        }
    }
}
